﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ICSharpCode.TreeView;
using EasyStep.TestExecution;
using WPF.TestEditor.Properties;
using System.Threading;
using EasyStep.TestExecution.Data;

namespace WPF.TestEditor.Controls.History
{
    public partial class HistoryView : UserControlBase
    {
        public HistoryView()
            : base()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(HistoryView_Loaded);    
            //contextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(contextMenuStrip_Opening);
            LeftColumnWidth = Settings.Default.HistoryTreeWidth;
            if (TestDataContext!=null)
                ReInitEditor();
        }

        public int LeftColumnWidth
        {
            get
            {
                return (int)grdLayout.ColumnDefinitions[0].Width.Value;
            }
            set
            {
                grdLayout.ColumnDefinitions[0].Width = new GridLength(value);
            }
        }

        
        #region initializations and commants
        
        private ContextMenu menu;
        public void ReInitEditor()
        {
            if (menu == null)
            {
                menu = new ContextMenu();                
                menu.SetResourceReference(Control.StyleProperty, "mentroContextMenu");
                menu.Items.Add(new MenuItem() { Icon = new Image() { Source = new BitmapImage(new Uri(GetNodeImage("openResult16.png"))) }, Header = "Open", Command = ApplicationCommands.Open });
                menu.Items.Add(new Separator());
                menu.Items.Add(new MenuItem() { Icon = new Image() { Source = new BitmapImage(new Uri(GetNodeImage("delete16.png"))) }, Header = "Delete", Command = ApplicationCommands.Delete });
                menu.Items.Add(new Separator());
                menu.Items.Add(new MenuItem() { Icon = new Image() { Source = new BitmapImage(new Uri(GetNodeImage("refresh16.png"))) }, Header = "Refresh", Command = ApplicationCommands.CorrectionList});
            }
            
            CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
                                                       new CommandBinding(ApplicationCommands.Delete, HandleExecuted_Delete, HandleCanExecute_Selected));
            CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
                                                       new CommandBinding(ApplicationCommands.Open, HandleExecuted_Open, HandleCanExecute_Selected));
            CommandManager.RegisterClassCommandBinding(typeof(SharpTreeView),
                                                       new CommandBinding(ApplicationCommands.CorrectionList, HandleExecuted_Refresh));
            //trvHistory.Items.Clear();
            if (trvHistory.Root == null)
                trvHistory.Root = new HistoryItemInfo(this, "Results:", GetNodeImage("history.png"), CreateLoadingNode()) { OnCollapsingAction = Node_Collapsed, OnExpandingAction = BindHistoryItems };
            trvHistory.Root.Children.Clear();            
            HistoryView_Loaded(this, null);
        }
        void HistoryView_Loaded(object sender, RoutedEventArgs e)
        {
            if (trvHistory.ContextMenu == null) trvHistory.ContextMenu = menu;
            if (TestDataContext==null || string.IsNullOrEmpty(TestDataContext.CurrentProject)) return;

            //select current history
            var curPrName = System.IO.Path.GetFileName((TestDataContext.CurrentProject ?? "").TrimEnd('\\'));
            var curTstName = System.IO.Path.GetFileName(TestDataContext.CurrentTest);
            SelectAndExpandNode(trvHistory.Root as HistoryItemInfo, () =>
            {
                foreach (HistoryItemInfo pnode in trvHistory.Root.Children)
                    if (pnode.Title.Equals(curPrName, StringComparison.OrdinalIgnoreCase))
                    {
                        SelectAndExpandNode(pnode, () =>
                        {
                            foreach (HistoryItemInfo tnode in pnode.Children)
                                if (tnode.Title.Equals(curTstName, StringComparison.OrdinalIgnoreCase))
                                {
                                    SelectAndExpandNode(tnode, () =>{
                                        var lastresult=tnode.Children.FirstOrDefault() as HistoryItemInfo;
                                        SelectAndExpandNode(lastresult, () =>{
                                            var topBrowser=lastresult.Children.FirstOrDefault() as HistoryItemInfo;
                                            SelectAndExpandNode(topBrowser, () => { });
                                        });
                                        if (lastresult != null)
                                           trvHistory.SelectedItem= lastresult;
                                    });
                                    break;
                                };
                        });
                        break;
                    }
            });
        }
        static void HandleExecuted_Delete(object sender, ExecutedRoutedEventArgs e)
        {
            SharpTreeView treeView = (SharpTreeView)sender;
            foreach (HistoryItemInfo node in treeView.GetTopLevelSelection().ToArray())
                ConfirmDelete("Confirm ...", node, () =>
                {
                    var p = node.Parent;
                    node.DeleteCore();
                    if (p != null)
                        p.Children.Remove(node);
                    else
                    {
                        node.Children.Clear();                       
                    };
                });
        }

        private static  void ConfirmDelete(string deleteType, HistoryItemInfo node, Action onConfirmed)
        {
            var parent = node.Parent;
            var nodesPath = node.Text;
            while (parent != null)
            {
                nodesPath = string.Format("[{0}] > {1}", parent.Text, nodesPath);
                parent = parent.Parent as HistoryItemInfo;
            }
            try
            {
                if (MessageBox.Show("Delete following result:\r\n" + nodesPath, deleteType, MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes) return; 
                onConfirmed();
                if (node != null && node.Parent != null)
                {
                    parent = node.Parent as HistoryItemInfo;
                    parent.IsSelected = true;
                    parent.IsExpanded = true;
                    parent.IsSelected = true;
                }
            }
            catch (Exception ex)
            {               
                MessageBox.Show("Can't Delete " + nodesPath + "\r\n\r\n" + ex.Message + "\r\n", "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        static void HandleCanExecute_Selected(object sender, CanExecuteRoutedEventArgs e)
        {
            SharpTreeView treeView = (SharpTreeView)sender;
            e.CanExecute = treeView.GetTopLevelSelection().Any();
            
        }
        static void HandleExecuted_Open(object sender, ExecutedRoutedEventArgs e)
        {
            SharpTreeView treeView = (SharpTreeView)sender;
            var node=treeView.GetTopLevelSelection().FirstOrDefault() as HistoryItemInfo;
            node.Open();
        }
        static void HandleExecuted_Refresh(object sender, ExecutedRoutedEventArgs e)
        {
            SharpTreeView treeView = (SharpTreeView)sender;
            var node = treeView.GetTopLevelSelection().FirstOrDefault() as HistoryItemInfo;
            node=node != null ? node : treeView.Root as HistoryItemInfo; 
            node.IsExpanded = false;
            node.IsExpanded = true;
            node.IsSelected = true;
        }
        #endregion
        private void BindHistoryItems(HistoryItemInfo current)
        {
            if (current != null) current.Children.Clear();
            var projNode = GetParentNode(current, 1);
            var testNode = projNode == null ? null : GetParentNode(current, 2);
            var sessionNode = testNode == null ? null : GetParentNode(current, 3);
            var browserNode = sessionNode == null ? null : GetParentNode(current, 4);
            var tstepNode = browserNode == null ? null : GetParentNode(current, 5);
            if (projNode== null)
                current.Children.AddRange(
                    TestDataContext.TestHistoryManager.GetAllProjects()
                   .Select(pname => new HistoryItemInfo(this, pname, GetNodeImage("project16.png"), CreateLoadingNode()) { OnCollapsingAction = Node_Collapsed, OnExpandingAction = BindHistoryItems })                   
               );
            else if (testNode==null)
                current.Children.AddRange(
                    TestDataContext.TestHistoryManager.GetProjectTests(projNode.Title)
                    .Select(tname => new HistoryItemInfo(this, tname, GetNodeImage("test16.png"), CreateLoadingNode()) {  OnCollapsingAction = Node_Collapsed, OnExpandingAction = BindHistoryItems })                   
                    .ToArray()
                );
            else if (sessionNode == null)
            {
                AbortThread(analizeThread);
                analizeThread = new Thread(() =>
                {
                   this.Dispatcher.Invoke(new Action(() =>
                    {
                        current.Children.AddRange(
                            TestDataContext.TestHistoryManager.GetTestResults(projNode.Title, testNode.Title)
                            .Select(rname => AnalizeTestResult(projNode.Title, testNode.Title, rname
                                ,
                                (result, childNodes) => new HistoryItemInfo(this,rname, GetImageIndex(result), childNodes) { Tag = 1 }
                                ,
                                (browsername, result) => new HistoryItemInfo(this, browsername, GetImageIndex(result), CreateLoadingNode()) {  OnCollapsingAction = Node_Collapsed, OnExpandingAction = BindHistoryItems }
                            )).ToArray()
                        );
                        if (current.OnChildrenAsyncChaged != null)
                            current.OnChildrenAsyncChaged(current);
                    }));
                });
                analizeThread.Start();
            }
            else if (tstepNode == null)
            {
                AbortThread(showNodesThread);
                showNodesThread = new Thread(() =>
                {
                    try
                    {
                        foreach (var stepResult in TestDataContext.TestHistoryManager.GetTestResultSteps(projNode.Title, testNode.Title, sessionNode.Title, browserNode.Title, false))
                        {
                            var node = ConvertStepResultToNode(stepResult);
                            if (node != null)
                                this.Dispatcher.Invoke(new Action(() => current.Children.Add(node)));
                        }
                        if (current.OnChildrenAsyncChaged != null)
                            current.OnChildrenAsyncChaged(current);
                    }
                    catch (Exception ex)
                    {
                        TestDataContext.Log.Fatal("can't show broser history!", ex);
                    }
                });
                showNodesThread.Start();
            }
        }
      
     
        private HistoryItemInfo ConvertStepResultToNode(TestStepResult stepResult)
        {
            if (stepResult == null) return null;
            var result = new HistoryItemInfo(this, stepResult.StepInfo.OrderNr.ToString(), GetImageIndex(stepResult.Result)) { Tag = 1, StepResult=stepResult};
            result.ToolTipText = TestDataContext.TestHistoryManager.Convert(stepResult);
            if (stepResult.Children == null) return result;
            foreach (var item in stepResult.Children)
            {
                var node = ConvertStepResultToNode(item);
                if (node != null)
                    result.Children.Add(node);
            }
            return result;
        }
        private T AnalizeTestResult<T>(string projName, string testName, string resName, Func<ExecutionResult, List<T>, T> onFinished, Func<string, ExecutionResult, T> onProcessResultItem)
        {
            var existFailed = false;
            var existInProgress = false;
            var subResults = new List<T>();
            var browserResults=TestDataContext.TestHistoryManager.GetTestResultBrowsers(projName, testName, resName);
            if (!browserResults.Any())
            {//no any browser result - test failed
                existFailed = true;
            }
            else 
                foreach (var britem in browserResults)
                {
                    try
                    {
                        var itemResult = AnalizeTestBrowserResult(britem.Value);
                        subResults.Add(onProcessResultItem(britem.Key, itemResult));
                        if (itemResult == ExecutionResult.Failed)
                            existFailed = true;
                        else if (itemResult == ExecutionResult.InProgress)
                            existInProgress = true;
                    }
                    catch (Exception ex)
                    {
                        TestDataContext.Log.Error(string.Format("Can't process browser result! proj:{0}, test:{1}, res:{2}, browser:{3}, error:{4}",
                            projName, testName, resName, britem.Key, ex.Message), ex);
                        existFailed = true;
                    }
                }
            var testResult = existFailed ? ExecutionResult.Failed : existInProgress ? ExecutionResult.InProgress : ExecutionResult.Success;
            var resultNode = onFinished(testResult, subResults);
            return resultNode;
        }
        private ExecutionResult AnalizeTestBrowserResult(IEnumerable<TestStepResult> results)
        {
            if (results==null || results.Count()==0 || results.Where(ri => ri.Result == ExecutionResult.Failed).Any())
                return ExecutionResult.Failed;
            if (results.Where(ri => ri.Result == ExecutionResult.InProgress).Any())
                return ExecutionResult.InProgress;
            return ExecutionResult.Success;
        }
        private void Node_Collapsed(HistoryItemInfo sender)
        {
            if (sender.Tag is int && (int)sender.Tag == 1) return;
            sender.Children.Clear();
            sender.Children.Add(CreateLoadingNode());
        }

        private HistoryItemInfo CreateLoadingNode()
        {
            return new HistoryItemInfo(this, "loading...", GetNodeImage("execute16.png"));
        }
        
        public HistoryItemInfo GetParentNode(HistoryItemInfo selectedNode, int level)
        {
            if (selectedNode == null) return null;
            var node = selectedNode;
            while (node != null && node.Level != level)
            {
                node = node.Parent as HistoryItemInfo;
            }
            return node;
        }
        
        Thread infoLoader;
        Thread analizeThread;
        Thread showNodesThread;        
        private void trvHistory_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            AbortThread(infoLoader);            
            infoLoader = new Thread((d) =>
            {
                var selitem = d as HistoryItemInfo;
                if (selitem != null) 
                   this.Dispatcher.Invoke(new Action(()=>{
                       var parent = selitem;
                       pnlCurrentPath.Children.Clear();
                       while (parent != null)
                       {
                           InserOnTopControl(pnlCurrentPath.Children, new TextBlock(), (c) =>
                           {                               
                               c.Text = parent.Title;
                               c.ToolTip = parent.ToolTipText;
                               c.OverridesDefaultStyle = true;
                               c.SetResourceReference(Control.StyleProperty, "historyPathInfoText");
                           }); 
                           InserOnTopControl(pnlCurrentPath.Children, new Image(), (c) =>
                           {
                               c.OverridesDefaultStyle = true;
                               c.SetResourceReference(Control.StyleProperty, "historyPathInfoIcon");
                               c.Source = new BitmapImage((Uri)parent.Icon);
                           });
                           parent = parent.Parent as HistoryItemInfo;
                           if (parent != null)
                               InserOnTopControl(pnlCurrentPath.Children, new TextBlock(), (c) =>
                               {
                                   c.OverridesDefaultStyle = true;
                                   c.SetResourceReference(Control.StyleProperty, "historyPathInfoSeparator");
                               });
                       }
                       RefreshResultDetail(selitem);
                   }));

            });
            infoLoader.Start(trvHistory.SelectedItem);
        }
        private void RefreshResultDetail(HistoryItemInfo selectedNode)
        {
            var projectNode = GetParentNode(selectedNode, 1);
            var testNode = GetParentNode(selectedNode, 2);
            if (testNode == null) return;
            var sessionNode = GetParentNode(selectedNode, 3);
            if (sessionNode == null) return;
            var browserNode = GetParentNode(selectedNode, 4);
            var stepNode = selectedNode;
            var executionPath = "";
            while (browserNode != null && stepNode.Level > browserNode.Level)
            {
                executionPath = System.IO.Path.Combine(stepNode.Title, executionPath);
                stepNode = stepNode.Parent as HistoryItemInfo;
            }
            executionPath = System.IO.Path.Combine(sessionNode.Title, executionPath);
            stackInfoPanel.Visibility=System.Windows.Visibility.Hidden;
            var results = (browserNode == null) ? null : TestDataContext.TestHistoryManager.GetTestResultSteps(projectNode.Title, testNode.Title, executionPath, browserNode.Title, true);
            //var actions = TestDataContext.ActionManager.GetAll();
            stackInfoPanel.Children.Clear();

            if (results != null)
            {
                var amount = 0;
                foreach (var ritem in results)
                {
                    amount++;
                    var rcontrol = new TestStepView(ritem.StepInfo);
                    //rcontrol.DoubleClick+=new EventHandler((o,a)=>OnDoubleClick(ritem,selectedNode));
                    stackInfoPanel.Children.Add(rcontrol);
                    rcontrol.SetResult(ritem);
                }
                if (amount == 0 && selectedNode.StepResult != null)
                {
                    var rcontrol = new TestStepView(selectedNode.StepResult.StepInfo);
                    stackInfoPanel.Children.Add(rcontrol);
                    rcontrol.SetResult(selectedNode.StepResult);
                    rcontrol.IsExpanded=true;
                }
            }
            else
                foreach (var brKVPair in TestDataContext.TestHistoryManager.GetTestResultBrowsers(projectNode.Title, testNode.Title, sessionNode.Title, false))
                    foreach (var ritem in brKVPair.Value)
                    {
                        var rcontrol = stackInfoPanel.Children.OfType<TestStepView>().Where(c => c.Id.Equals(ritem.StepInfo.Id)).FirstOrDefault();
                        if (rcontrol == null)
                        {
                            rcontrol = new TestStepView(ritem.StepInfo);
                            //rcontrol.DoubleClick += new EventHandler((o, a) => OnDoubleClick(ritem, selectedNode));
                            stackInfoPanel.Children.Add(rcontrol);
                            rcontrol.SetResult(ritem);
                        }
                        else
                            rcontrol.SetResult(ritem);
                    }
            stackInfoPanel.Visibility = System.Windows.Visibility.Visible;
        }

        private void OnDoubleClick(TestStepResult resultitem, HistoryItemInfo parentNode)
        {

            ///TODO:it's doesn't work yet
            if (parentNode==null) return;
            parentNode.IsSelected = true;
            if (!parentNode.IsExpanded) parentNode.IsExpanded = true;
            parentNode.IsSelected = true;
            var child=parentNode.Children.OfType<HistoryItemInfo>()
                .Where(itm => itm.Title.Equals(resultitem.StepInfo.OrderNr))
                .FirstOrDefault();
            if (child != null)
            {
                child.IsSelected = true;
                child.IsExpanded = true;
                child.IsSelected = true;
            }
        }
        
        #region help methods
        private void SelectAndExpandNode(HistoryItemInfo node, Action onFinish)
        {
            if (node == null) return;
            if (node.IsSelected)
                node.IsSelected = false;
            node.IsSelected = true;
            var oldExpanding=node.OnExpandingAction;
            node.OnExpandingAction = (nnode) =>
            {//expand node synch
                nnode.OnExpandingAction = oldExpanding;
                if(oldExpanding!=null) oldExpanding(nnode);
                if (nnode.Children.Count > 0)
                    onFinish();     
            };
            var oldOnAsyncChaged = node.OnChildrenAsyncChaged;
            node.OnChildrenAsyncChaged = (nnode) =>
            {//expand node asynch
                nnode.OnChildrenAsyncChaged = oldOnAsyncChaged;
                if (oldOnAsyncChaged != null) oldOnAsyncChaged(nnode);
                if (nnode.Children.Count > 0)
                    onFinish();
            };
            if (node.IsExpanded)
                node.IsExpanded = false;
            node.IsExpanded = true;
            
        }
        private T InserOnTopControl<T, C>(C collection, T control, Action<T> onCreated)
            where T : FrameworkElement
            where C : System.Collections.IList
        {
            collection.Insert(0, control);
            onCreated(control);
            return control;
        }
        private string GetImageIndex(ExecutionResult result, bool forSelectedNode = false)
        {
            return GetNodeImage(result.ToString() + "16.png", forSelectedNode);
        }
        private string GetNodeImage(string key, bool selected = false)
        {
            return System.IO.Path.Combine(Settings.Default.ResourceFolder, key);
        }

        private void AbortThread(Thread thread)
        {
            if (thread != null && thread.IsAlive)
                thread.Abort();
        }

        #endregion
    }
}
